package com.cwn.wethink.common.utils.file;

import com.cwn.wethink.common.config.ApplicationProperties;
import com.cwn.wethink.common.enums.ErrorCodeEnum;
import com.cwn.wethink.common.exception.MessageException;
import com.cwn.wethink.common.utils.SpringContextUtil;
import com.cwn.wethink.common.utils.StringUtils;
import com.cwn.wethink.file.api.domain.request.SimpleCopyRequest;
import com.cwn.wethink.file.api.service.OssFileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.FileUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 文件操作类
 *
 * @author leon.li
 * @date 2014-8-7 下午6:04:20
 */
@Slf4j
public class FileUtil extends FileUtils {

    /**
     * 复制文件
     *
     * @param targetFile unknown
     * @param file       unknown
     */
    public static void fileChannelCopy(File targetFile, File file) {
        FileChannel in = null;
        FileChannel out = null;
        try (FileInputStream fi = new FileInputStream(targetFile);
             FileOutputStream fo = new FileOutputStream(file);) {
            in = fi.getChannel();// 得到对应的文件通道
            out = fo.getChannel();// 得到对应的文件通道
            in.transferTo(0, in.size(), out);// 连接两个通道，并且从in通道读取，然后写入out通道
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
            if (null != out) {
                try {
                    out.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
    }

    public static void delFiles(String srcDirPath) throws IOException {
        File dir = new File(srcDirPath);
        if (!dir.exists()) {
            return;
        }
        String[] fList = null;
        File fh = null;
        if (dir.isDirectory() && dir.list() != null) {
            fList = dir.list();
            for (int i = 0; i < fList.length; i++) {
                fh = new File(dir, fList[i]);
                if (fh.isDirectory()) {
                    delDir(srcDirPath + File.separator + fList[i]);
                } else {
                    if (fh.exists()) {
                        Files.delete(fh.toPath());
                    }
                }
            }
        }
    }

    public static void delDir(String srcDirPath) throws IOException {
        File dir = new File(srcDirPath);
        if (!dir.exists()) {
            return;
        }
        String[] fList;
        File fh = null;
        if (dir.isDirectory()) {
            fList = dir.list();
            for (int i = 0; i < fList.length; i++) {
                fh = new File(dir, fList[i]);
                if (fh.isDirectory()) {
                    delDir(srcDirPath + File.separator + fList[i]);
                } else {
                    if (fh.exists()) {
                        Files.delete(fh.toPath());
                    }
                }
            }
            if (dir.exists()) {
                Files.delete(dir.toPath());
            }
        }
    }

    public static void copyDir(String srcDir, String toDir)
            throws IOException {

        File fromDir = new File(srcDir);
        File saveDir = null;
        if (fromDir.exists()) {
            saveDir = new File(toDir);
            if (!saveDir.exists()) {
                saveDir.mkdirs();
            }
        } else {
            return;
        }

        String[] flList = fromDir.list();
        File oF = null;
        File nF = null;
        try {
            for (int i = 0; i < flList.length; i++) {
                oF = new File(fromDir, flList[i]);

                if (oF.isDirectory()) {
                    String subSrcDir = srcDir + File.separator + flList[i];
                    String subToDir = toDir + File.separator + flList[i];
                    FileUtil.copyDir(subSrcDir, subToDir);
                } else {
                    nF = new File(saveDir, oF.getName());
                    if (nF.exists()) {
//                        Files.delete(nF.toPath());
                        nF = new File(saveDir, oF.getName());
                    }
                    copyFile(oF, nF);
                }
            }
        } catch (IOException e) {
            log.error("IO Error: " + e.getMessage());
            throw new IOException(ErrorCodeEnum.IO_ERROR.getCode());
        }
    }

    /**
     * 根据文件夹获取其下的文件
     *
     * @param directoryPath 文件夹路径
     * @return 文件集合
     */
    public static File[] getFileByDirectory(String directoryPath) {
        File directory = new File(directoryPath);
        File[] listFiles = new File[5];
        if (directory.isDirectory()) {
            listFiles = directory.listFiles();
        }
        return listFiles;
    }

    /**
     * 获取资源 - SCO课件上传文件
     *
     * @param directoryPath 文件路径文件夹
     * @return 上传文件
     */
    public static File getUploadScoFile(String directoryPath) {
        File[] list = getFileByDirectory(directoryPath);
        return list[0];
    }

    /**
     * 将File转换成MultipartFile
     *
     * @param file File文件
     * @return MultipartFile
     */
    public static MultipartFile getMultipartFileByFile(File file) {
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        String textFieldName = "textField";
        FileItem item = factory.createItem(textFieldName, "text/plain", true, file.getName());
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        OutputStream os = null;
        try (FileInputStream fis = new FileInputStream(file);) {
            os = item.getOutputStream();
            while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
        } catch (FileNotFoundException fileNotFound) {
            fileNotFound.printStackTrace();
            throw new MessageException(ErrorCodeEnum.ERROR_FILE_NOT_FOUND);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != os) {
                try {
                    os.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
        return new CommonsMultipartFile(item);
    }

    /**
     * 检查目录是否存在、如不存在、则创建
     *
     * @param filePath 目录路径
     */
    public static void checkDirectory(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
    * 实现功能描述：把文件复制到指定目录
    * @param srcPath 文件路径
    * @param destDir 目录
    * @param isDelete 是否删除原文件、true-是、false-否
    * @return 文件名
    */
    public static String copyFileToDir(String srcPath, String destDir, boolean isDelete) {
        File fromDir = new File(srcPath);
        File destFile ;
        File toDir = new File(destDir);
        if (!toDir.exists()) {
            toDir.mkdirs();
            destFile = new File(destDir, fromDir.getName());
        } else {
            destFile = new File(destDir, fromDir.getName());
            if (destFile.exists()) {
                if (fromDir.exists()) {
                    destFile.delete();
                } else {
                    return destFile.getName();
                }
            } else {
                return destFile.getName();
            }
        }
        if (!fromDir.exists()) {
            throw new MessageException(ErrorCodeEnum.ERROR_FILE_NOT_FOUND);
        }
        try {
            if (fromDir.getCanonicalPath().equals(destFile.getCanonicalPath())) {
                throw new MessageException(ErrorCodeEnum.ERROR_FILE_EXISTS);
            }
            FileUtils.copyFile(fromDir, destFile);
            // 删除被复制的文件
            if (isDelete) {
                Files.delete(fromDir.toPath());
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new MessageException(ErrorCodeEnum.IO_ERROR);
        }
        return destFile.getName();
    }

    /**
     * 实现功能描述：把文件复制到指定目录
     * @param srcPath 文件路径
     * @param destDir 目录
     * @return 文件名
     */
    public static String copyFileToDir(String srcPath, String destDir) {
        File fromDir = new File(srcPath);
        if (!fromDir.exists()) {
            throw new MessageException(ErrorCodeEnum.ERROR_FILE_NOT_FOUND);
        }
        File toDir = new File(destDir);
        if (!toDir.exists()) {
            toDir.mkdirs();
        }
        File destFile = new File(destDir, fromDir.getName());
        try {
            FileUtils.copyFile(fromDir, destFile);
            // 删除被复制的文件
            Files.delete(fromDir.toPath());
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new MessageException(ErrorCodeEnum.IO_ERROR);
        }
        return destFile.getName();
    }

    /**
    * 实现功能描述：把文件复制到指定目录
    * @param srcPath 文件路径
    * @param destDir 目录
    * @param isThrowException 是否需要抛出文件不存在错误
    * @return 文件名
    */
    public static String copyFileToDir(String srcPath, String destDir, Boolean isThrowException) throws IOException {
        File fromDir = new File(srcPath);
        if (!fromDir.exists() && isThrowException) {
            throw new IOException(ErrorCodeEnum.IO_ERROR.getCode());
        } else if (!fromDir.exists() && !isThrowException) {
            return null;
        }
        File toDir = new File(destDir);
        if (!toDir.exists()) {
            toDir.mkdirs();
        }
        File destFile = new File(destDir, fromDir.getName());
        try {
            FileUtils.copyFile(fromDir, destFile);
            // 删除被复制的文件
            Files.delete(fromDir.toPath());
        } catch (IOException e) {
            throw new IOException(ErrorCodeEnum.IO_ERROR.getCode());
        }
        return destFile.getName();
    }



    /**
     * 实现功能描述：删除文件/文件夹(含不为空的文件夹)
     *
     * @param dir
     * @throws IOException
     * @see Files#deleteIfExists(Path)
     * @see Files#walkFileTree(Path, java.nio.file.FileVisitor)
     */
    public static void deleteIfExists(Path dir) throws IOException {
        try {
            Files.deleteIfExists(dir);
        } catch (DirectoryNotEmptyException e) {
            Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Files.delete(file);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    Files.delete(dir);
                    return super.postVisitDirectory(dir, exc);
                }
            });
        }
    }

    /**
     * 移动文件
     * @param srcFile 来源文件地址
     * @param destFile 目标文件地址
     */
    public static void moveFile(String srcFile, String destFile) throws IOException {
        File fromFile = new File(srcFile);
        if (!fromFile.exists()) {
            return;
        }
        File toFile = new File(destFile);
        if (toFile.exists()) {
            Files.delete(toFile.toPath());
            toFile = new File(destFile);
        }
        try (RandomAccessFile f1 = new RandomAccessFile(fromFile, "r");
             RandomAccessFile f2 = new RandomAccessFile(toFile, "rw");) {
            int length = (int) f1.length();
            byte[] buffer1 = new byte[length];
            f1.readFully(buffer1, 0, length);
            f2.write(buffer1, 0, length);
        } finally {
            Files.delete(fromFile.toPath());
        }
    }

    /**
     * 复制来根据图片路径移动到指定目录。所有的都只保留文件名
     *
     * @param props    目录配置文件类
     * @param tempUrl          图片路径
     * @param targetDirAbsPath 保存的文件夹
     * @param isClearDir   是否删除临时文件
     * @return String
     */
    public static String moveFile(ApplicationProperties props, String tempUrl, String targetDirAbsPath, boolean isClearDir) {
        if (StringUtils.isEmpty(tempUrl) || StringUtils.isEmpty(targetDirAbsPath)) {
            return null;
        }
        //所有的都只保留文件名
        String picName = getUrlFileName(tempUrl);
        try {
            ApplicationProperties.Directory directory = props.getDirectory();

            String itmIconPath = null;
            //itmIcon 处理课程图片
            boolean fromImgLib = false;
            if (tempUrl.indexOf(directory.getTempDir().getWebPath()) == 0) {
                itmIconPath = directory.getTempDir().getAbsPath() + picName;
            } else if ((tempUrl.indexOf(directory.getImgLib().getWebPath()) == 0)
                    || (tempUrl.indexOf(directory.getItmDir().getWebPath()) == 0)) {
                //允许从别的课程目录复制图片

                fromImgLib = true;
                String basePath = directory.getBaseDir().getFile().getParent();
                itmIconPath = basePath + File.separator + tempUrl.replaceAll("\\\\/", File.separator);
            }

            if (itmIconPath != null) {
                File toDir = new File(targetDirAbsPath);
                if (!toDir.exists()) {
                    toDir.mkdirs();
                } else if (isClearDir) {
                    FileUtil.delDir(targetDirAbsPath);
                }
                if (fromImgLib) {
                    //拷贝
                    FileUtils.copyFile(new File(itmIconPath), new File(targetDirAbsPath, picName));
                } else {
                    //移动
                    FileUtils.moveFile(new File(itmIconPath), new File(targetDirAbsPath, picName));
                }
            }

            //TODO 重构
            OssFileService ossFileService = (OssFileService) SpringContextUtil.getBean("ossFileServiceImpl");
            if (ossFileService != null) {
                String basePath = directory.getBaseDir().getFile().getParent();
                String toUrl = targetDirAbsPath.replace(basePath, "") + "/" + picName;
                // /resource/temp/1.jpg ---->   /resource/item/1287/2.jpg
                ossFileService.copyFile(new SimpleCopyRequest(tempUrl, toUrl));
            }
            return picName;
        } catch (Exception e) {
            log.error("move file from url path error {}", e.getMessage(), e);
        }
        return picName;
    }

    /**
     * 获取文件名字
     * @param iconUrl 文件url
     * @return
     */
    public static String getUrlFileName(String iconUrl) {
        if (StringUtils.isEmpty(iconUrl)) {
            return iconUrl;
        }
        return iconUrl.substring(iconUrl.lastIndexOf("/") + 1);
    }

    public static void copyFiles(List<File> files, String targetDirectory, Map<String, String> nameMap) {
        if (files!= null) {
            for (File file : files) {
                if (file.isFile()) {
                    try {
                        Path sourcePath = file.toPath();
                        String targetName = file.getName();
                        //根据指定的名字复制文件
                        if (nameMap != null && nameMap.containsKey(file.getName())){
                            targetName = nameMap.get(file.getName());
                        }
                        Path targetPath = new File(targetDirectory, targetName).toPath();
                        //名字冲突时重命名
                        if (Files.exists(targetPath)){
                            targetPath = generateNonConflictPath(targetPath);
                        }
                        // 使用 Files.copy 方法复制文件
                        Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
                        System.out.println("Copied " + file.getName() + " to " + targetDirectory);
                    } catch (IOException e) {
                        System.err.println("Failed to copy " + file.getName() + ": " + e.getMessage());
                    }
                }
            }
        }
    }

    /**
     * 生成不冲突的文件名
     * @param path 原始文件路径
     * @return 不冲突的新路径
     */
    public static Path generateNonConflictPath(Path path) {
        if (!Files.exists(path)) {
            return path;
        }

        String fileName = path.getFileName().toString();
        Path parent = path.getParent();

        // 匹配"文件名(序号).扩展名"格式
        Pattern pattern = Pattern.compile("(.*?)\\((\\d+)\\)\\.(.*)");
        Matcher matcher = pattern.matcher(fileName);

        if (matcher.matches()) {
            String baseName = matcher.group(1);
            int currentNum = Integer.parseInt(matcher.group(2));
            String extension = matcher.group(3);

            // 尝试增加序号直到找到不存在的文件名
            Path newPath;
            do {
                currentNum++;
                newPath = parent.resolve(String.format("%s(%d).%s", baseName, currentNum, extension));
            } while (Files.exists(newPath));

            return newPath;
        } else {
            // 没有序号时，添加"(1)"
            int dotIndex = fileName.lastIndexOf('.');
            String baseName, extension;

            if (dotIndex > 0) {
                baseName = fileName.substring(0, dotIndex);
                extension = fileName.substring(dotIndex + 1);
            } else {
                baseName = fileName;
                extension = "";
            }

            // 尝试增加序号直到找到不存在的文件名
            Path newPath;
            int num = 1;
            do {
                newPath = parent.resolve(extension.isEmpty() ?
                        String.format("%s(%d)", baseName, num) :
                        String.format("%s(%d).%s", baseName, num, extension));
                num++;
            } while (Files.exists(newPath));

            return newPath;
        }
    }

}
